﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
using System.Linq.Dynamic;

namespace Green.Utility
{
    public class DynamicExpression
    {
        private static Dictionary<ComputeExpressionKey, LambdaExpression> dict = new Dictionary<ComputeExpressionKey, LambdaExpression>();

        private static readonly object lockobj = new object();
        public static object Compute(object model, Type resultType, string expression)
        {
            if (Green.FastInvoker.CustomTypeDescriptorHelper.IsCustomTypeDescriptor(model))
            {
                return Compute(model as System.ComponentModel.ICustomTypeDescriptor, resultType, expression);
            }
            return ComputeEntity(model, resultType, expression);
        }

        protected static object ComputeEntity(object model, Type resultType, string expression)
        {
            var data = model;
            var key = new ComputeExpressionKey(model.GetType(), resultType, expression);
            if (dict.Keys.Contains(key, new ComputeExpressionKeyEqualityComparer()))
            {
                return Compute(data, dict[key]);
            }

            lock (lockobj)
            {
                if (dict.Keys.Contains(key, new ComputeExpressionKeyEqualityComparer()))
                {
                    return Compute(data, dict[key]);
                }
                dict.Add(key, ParseExpression(data, resultType, expression));
                return Compute(data, dict[key]);
            }
        }

        public static object Compute(System.Data.DataRow model, Type resultType, string expression)
        {
            Dictionary<string, object> values = new Dictionary<string, object>();
            List<System.Linq.Dynamic.DynamicProperty> property = new List<DynamicProperty>();
            foreach (System.Data.DataColumn item in model.Table.Columns)
            {
                values.Add(item.ColumnName, model[item] == DBNull.Value ? null : model[item]);
            }
            return Compute(values, resultType, expression);
        }

        public static object Compute(System.ComponentModel.ICustomTypeDescriptor model, Type resultType, string expression)
        {
            var ps = Green.FastInvoker.CustomTypeDescriptorHelper.GePropertyDescriptors(model);
            Dictionary<string, object> values = new Dictionary<string, object>();
            List<System.Linq.Dynamic.DynamicProperty> property = new List<DynamicProperty>();
            foreach (System.ComponentModel.PropertyDescriptor item in ps)
            {
                values.Add(item.Name, item.GetValue(model));
            }
            return Compute(values, resultType, expression);
        }

        public static object Compute(Dictionary<string, object> dictProperty, Type resultType, string expression)
        {
            object data = null;
            List<System.Linq.Dynamic.DynamicProperty> property = new List<DynamicProperty>();
            dictProperty.ToList().ForEach(item => property.Add(new DynamicProperty(item.Key, item.Value.GetType())));
            var nonType = System.Linq.Dynamic.ClassFactory.Instance.GetDynamicClass(property);
            data = Activator.CreateInstance(nonType);
            dictProperty.ToList().ForEach(item => Green.FastInvoker.PropertyAccessor.Set(data, item.Key, item.Value));
            return ComputeEntity(data, resultType, expression);
        }

        public static LambdaExpression ParseExpression(object model, Type resultType, string expression)
        {
            return System.Linq.Dynamic.DynamicExpression.ParseLambda(model.GetType(), resultType, expression);
        }

        public static object Compute(object model, LambdaExpression computeExpression)
        {
            return computeExpression.Compile().DynamicInvoke(model);
        }


        public static T Compute<T>(object model, string expression)
        {
            return (T)Compute(model, typeof(T), expression);
        }

        public static T Compute<T>(System.Data.DataRow model, string expression)
        {
            return (T)Compute(model, typeof(T), expression);
        }

        public static T Compute<T>(System.ComponentModel.ICustomTypeDescriptor model, string expression)
        {
            return (T)Compute(model, typeof(T), expression);
        }

        public static T Compute<T>(Dictionary<string, object> dictProperty, string expression)
        {
            return (T)Compute(dictProperty, typeof(T), expression);
        }     
     

         public static T Compute<T>(object model, LambdaExpression computeExpression)
        {
            return (T)computeExpression.Compile().DynamicInvoke(model);
        }
    }
}